#Copyright Okhtay Shoghi 2008
require 'thread'

class WebUtils
	@@counts = nil
	@@mutex = Mutex.new
	
	def self.reset_request_counts
		@@counts = {
			'delete'	=> {'sent' => 0, 'rcvd' => 0},
			'get'		=> {'sent' => 0, 'rcvd' => 0},
			'post'		=> {'sent' => 0, 'rcvd' => 0},
			'put'		=> {'sent' => 0, 'rcvd' => 0},
			'total'		=> {'sent' => 0, 'rcvd' => 0},
			'results'	=> {},
			'calls'		=> {},
		}
	end

	def self.get_request_counts
		return @@counts.clone
	end

	def self.inc_req_count(method, direction="sent")
		@@counts[method][direction] += 1
		@@counts['total'][direction] += 1
	end
	
	def self.send(url, opts={})
		opts.defaults "method" => "get",
			"body" => {}, 
			"json" => false,
			"username" => nil,
			"password" => nil,
			"useragent" => "ruby",
			"caller" => "undef",
			"decode" => "no"
		
		body = self.req(url, opts['method'], opts['body'], opts['json'], opts['username'], opts['password'], opts['useragent'], opts['caller'])
		
		if (opts['decode'] == "json")
			begin
				return JSON.parse(body)
			rescue Exception => e
				return body
			end
		end
		
		return body
	end

	def self.req(url, method="get", post={}, json=false, username=nil, password=nil, ua="ruby", caller='undef')
		require 'openssl'
		require 'net/https'
		require 'uri' 
		
		body_text	= ''
		uri		 	= URI.parse(url)
		http		= Net::HTTP.new(uri.host, uri.port)
		req			= nil
		path		= uri.path
		path	   += "?"+uri.query if uri.query
		
		if (uri.scheme == 'https' || uri.port.to_i == 443)
			http.use_ssl		= true
			#http.verify_mode	= OpenSSL::SSL::VERIFY_NONE 
		end

		self.reset_request_counts if (@@counts == nil)
		self.inc_req_count(method)

		http.set_debug_output $stderr if ENABLE_HTTP_DEBUG
		http.read_timeout = 600

		start_time = Time.new

		begin
			http.start {|w|
				case(method)
					when "post","put"
						if (post.class == Hash)
							params = []
							post.each do |k,v|
								params << "#{k}=#{v}"
							end
							post = params.join("&")
						end
						eval("req = Net::HTTP::#{method.capitalize}.new(path)")
						req.body = post
					when "delete"
						eval("req = Net::HTTP::#{method.capitalize}.new(path)")
					else
						req = Net::HTTP::Get.new(path)
				end
							
				req.add_field("Content-Type", "application/json") if json
				req.add_field("User-Agent",ua) if ua
				req.basic_auth(username, password) if (username)
				response = http.request(req)
				
				Log.debug "HTTP Status Code: #{response.code}"
				$last_http_code = response.code
				body_text = response.body.to_s
			}	
		rescue Net::HTTPRetriableError => e
			body_text =	 self.post(response.header['location'])
		rescue Exception => e
			puts e.inspect
			body_text = e.to_s
		end

		elapsed_time = Time.new - start_time

		@@mutex.synchronize do
			@@counts['results'][$last_http_code] = Array.new unless @@counts['results'][$last_http_code]
			@@counts['results'][$last_http_code][0] ||= 0
			@@counts['results'][$last_http_code][0] += 1
			@@counts['calls'][caller] = [] if !@@counts['calls'][caller]
			@@counts['calls'][caller].push(elapsed_time)
		end

		Log.debug "#{body_text}\n\n"
		self.inc_req_count(method, 'rcvd')
		return body_text
	end

	def self.get(url)
		return self.req(url, "get")
	end

	def self.post(url, params={}, json=false)
		return self.req(url, "post", params, json)
	end 

	def self.put(url, params={}, json=false)
		return self.req(url, "put", params, json)
	end

	def self.delete(url)
		return self.req(url, "delete")
	end

	def self.json_request(url, method="get", data=nil, json=false)
		Log.debug "#{method.upcase} #{url} - #{data.inspect}"
		resp = do_request(url, method, data, json)
		begin
			return JSON.parse(resp)
		rescue Exception => e
			Log.debug e.inspect
			assert(false, resp)
		end
	end
end
